package org.study.redis.boot.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.*;
import tools.jackson.databind.DefaultTyping;
import tools.jackson.databind.introspect.VisibilityChecker;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;

import java.nio.charset.StandardCharsets;

/**
 * Redis 配置类。
 * <p>
 * StringRedisTemplate 与 RedisTemple 的区别：
 * 1、StringRedisTemplate 继承自 RedisTemple
 * 2、数据不共通，即：StringRedisTemplate 只能管理 StringRedisTemplate 的数据，RedisTemplate 只能管理 RedisTemplate 的数据
 * 3、spring-boot-starter-data-redis 默认的序列化方式有两种：String 序列化策略、JDK 序列化策略
 * StringRedisTemplate 默认采用的是 String 序列化策略，保存的 key、value 都是采用该策略序列化并保存的；
 * RedisTemplate 默认采用的是 JDK 序列化策略，保存的 key、value 都是采用此策略序列化并保存的
 *
 * @author Administrator
 * @date 2021-06-02
 */
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // Jdk 序列化器
        RedisSerializer jdkSerializationRedisSerializer = this.jdkSerializationRedisSerializer();
        // String 序列化器
        StringRedisSerializer stringRedisSerializer = this.stringRedisSerializer();
        // Jackson 序列化器
        RedisSerializer jacksonJsonRedisSerializer = this.jacksonJsonRedisSerializer();
        // FastJson 序列化器
        RedisSerializer fastJson2JsonRedisSerializer = this.fastJson2JsonRedisSerializer();
        // key 采用 String 的序列化方式，默认采用的是 JDK 序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // value 采用 jackson 的序列化方式，默认采用的是 JDK 序列化方式
        redisTemplate.setValueSerializer(jacksonJsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jacksonJsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplates(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate stringRedisTemplates = new StringRedisTemplate();
        stringRedisTemplates.setConnectionFactory(redisConnectionFactory);
        // Jdk 序列化器
        RedisSerializer jdkSerializationRedisSerializer = this.jdkSerializationRedisSerializer();
        // String 序列化器
        StringRedisSerializer stringRedisSerializer = this.stringRedisSerializer();
        // Jackson 序列化器
        RedisSerializer jacksonJsonRedisSerializer = this.jacksonJsonRedisSerializer();
        // FastJson 序列化器
        RedisSerializer fastJson2JsonRedisSerializer = this.fastJson2JsonRedisSerializer();
        // key 采用 String 的序列化方式，默认采用的是 JDK 序列化方式
        stringRedisTemplates.setKeySerializer(stringRedisSerializer);
        stringRedisTemplates.setHashKeySerializer(stringRedisSerializer);
        // value 采用 jackson 的序列化方式，默认采用的是 JDK 序列化方式
        stringRedisTemplates.setValueSerializer(jacksonJsonRedisSerializer);
        stringRedisTemplates.setHashValueSerializer(jacksonJsonRedisSerializer);
        stringRedisTemplates.afterPropertiesSet();
        return stringRedisTemplates;
    }

    /**
     * 对 redis 字符串类型数据操作：https://www.jianshu.com/p/5596c3a4978d
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        return valueOperations;
    }

    /**
     * 对 hash 类型的数据操作：https://www.jianshu.com/p/5596c3a4978d
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public HashOperations<String, Object, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        return hashOperations;
    }

    /**
     * 对链表类型的数据操作：https://www.jianshu.com/p/5596c3a4978d
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
        ListOperations<String, Object> listOperations = redisTemplate.opsForList();
        return listOperations;
    }

    /**
     * 对无序集合类型的数据操作：https://www.jianshu.com/p/5596c3a4978d
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
        SetOperations<String, Object> setOperations = redisTemplate.opsForSet();
        return setOperations;
    }

    /**
     * 对有序集合类型的数据操作：https://www.jianshu.com/p/5596c3a4978d
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
        ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
        return zSetOperations;
    }

    @Bean
    public ClusterOperations<String, Object> clusterOperations(RedisTemplate<String, Object> redisTemplate) {
        ClusterOperations<String, Object> clusterOperations = redisTemplate.opsForCluster();
        return clusterOperations;
    }

    @Bean
    public GeoOperations<String, Object> geoOperations(RedisTemplate<String, Object> redisTemplate) {
        GeoOperations<String, Object> geoOperations = redisTemplate.opsForGeo();
        return geoOperations;
    }

    @Bean
    public HyperLogLogOperations<String, Object> hyperLogLogOperations(RedisTemplate<String, Object> redisTemplate) {
        HyperLogLogOperations<String, Object> hyperLogLogOperations = redisTemplate.opsForHyperLogLog();
        return hyperLogLogOperations;
    }

    @Bean
    public StreamOperations<String, Object, Object> streamOperations(RedisTemplate<String, Object> redisTemplate) {
        StreamOperations<String, Object, Object> streamOperations = redisTemplate.opsForStream();
        return streamOperations;
    }

    /**
     * 配置 Jdk 序列化器
     */
    private RedisSerializer jdkSerializationRedisSerializer() {
        JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
        return jdkSerializationRedisSerializer;
    }

    /**
     * 配置 String 序列化器
     */
    private StringRedisSerializer stringRedisSerializer() {
        // StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(StandardCharsets.UTF_8);
        return stringRedisSerializer;
    }

    /**
     * 配置 Jackson 序列化器
     */
    private RedisSerializer jacksonJsonRedisSerializer() {
        JsonMapper jsonMapper = JsonMapper.builder()
                // 开启"默认类型信息"，仅作用于非 final 类
                .activateDefaultTyping(BasicPolymorphicTypeValidator.builder()
                                .allowIfSubType(Object.class)
                                .build(),
                        DefaultTyping.NON_FINAL,
                        JsonTypeInfo.As.PROPERTY
                )
                .changeDefaultVisibility(visibilityChecker -> VisibilityChecker.defaultInstance()
                        // .withFieldVisibility(Visibility.ANY)
                        // .withGetterVisibility(Visibility.ANY)
                        // .withIsGetterVisibility(Visibility.ANY)
                        // .withSetterVisibility(Visibility.ANY)
                        // .withCreatorVisibility(Visibility.ANY)
                        .with(JsonAutoDetect.Visibility.ANY)
                )
                .build();
        JacksonJsonRedisSerializer<Object> jacksonJsonRedisSerializer = new JacksonJsonRedisSerializer<>(jsonMapper, Object.class);
        return jacksonJsonRedisSerializer;
    }

    /**
     * 配置 FastJson 序列化器
     */
    private RedisSerializer fastJson2JsonRedisSerializer() {
        FastJson2JsonRedisSerializer fastJson2JsonRedisSerializer = new FastJson2JsonRedisSerializer(Object.class);
        return fastJson2JsonRedisSerializer;
    }
}
